#pragma once

#include <string>
#include <vector>
#include <stack>
#include <list>
#include <fstream>
#include <iostream>

#define CP_UTF8

#include "../json/json.h"
// #include "rapidjson/istreamwrapper.h"

#define SIMPLE_DESERIALIZE(x) v.deserialize(#x, x);
#define SIMPLE_DESERIALIZE_OPTIONAL(x) v.deserializeOptional(#x, x);

struct Object
{
	Object()
	{
		type = TYPE_ERR;
	}

	enum ObjectValueType
	{
		TYPE_ERR = 0,
		TYPE_INT,
		TYPE_STRING,
		TYPE_VECTOR,
		TYPE_NULL
	};

	ObjectValueType type;
	int int_val;
	std::string string_val;
	std::vector<Object> vector_val;
};

class JsonDeserializer
{
public:
	JsonDeserializer(const char *path);
	JsonDeserializer(const std::string &jsonString);
	~JsonDeserializer() {}

	template <typename T>
	void Deserialize(T &value)
	{
		try
		{
			value.deserialize(*this);
		}
		catch (std::string e)
		{
			std::cout << "Deserialize 异常: " << e << std::endl;
		}
		catch (...)
		{
			std::cout << "Deserialize 异常: 其他异常" << std::endl;
		}
	}

	template <typename T>
	void deserialize(std::string name, T &value)
	{
		info_.push_back(name);

		const Json::Value &json_parent = *value_stack_.top();

		// if (!json_parent.HasMember(name.c_str()))
		if (!json_parent.isMember(name))
		{
			std::cout << "!!! warning, deserialize,has no merber:" << name << std::endl;
			return;
		}
		const Json::Value &json_value = json_parent[name.c_str()];
		value_stack_.push(&json_value);
		try
		{
			deserialize(value);
		}
		catch (std::string e)
		{
			throw "Error: " + name + ":" + e;
		}
		value_stack_.pop();

		info_.pop_back();
	}

	template <typename T>
	void deserializeOptional(std::string name, T &value)
	{
		info_.push_back(name);

		const Json::Value &json_parent = *value_stack_.top();

		// if (!json_parent.HasMember(name.c_str()))
		if (!json_parent.isMember(name))
		{
			info_.pop_back();
			return;
		}

		const Json::Value &json_value = json_parent[name.c_str()];
		value_stack_.push(&json_value);

		try
		{
			deserialize(value);
		}
		catch (std::string e)
		{
			throw "Error: " + name + ":" + e;
		}
		// deserialize(value);

		value_stack_.pop();

		info_.pop_back();
	}

	template <typename T>
	void deserialize(std::string name, std::vector<T> &values)
	{
		info_.push_back(name);

		const Json::Value &json_parent = *value_stack_.top();

		// if (!json_parent.HasMember(name.c_str()))
		if (!json_parent.isMember(name))
		{
			std::cout << "!!! warning, deserialize,has no merber:" << name << std::endl;
			return;
			// std::string err_info = "Load json file [" + file_path_ + "] error, [" + GetParsePath() + "] doesn't exist!";
			// throw err_info;
		}

		const Json::Value &json_array = json_parent[name];

		// if (!json_array.IsArray())
		if (!json_array.isArray())
		{
			std::string err_info = "[" + name + "] is not an Array!";
			throw err_info;
		}

		// for (size_t i = 0; i < json_array.Size(); i++)
		for (int i = 0; i < json_array.size(); i++)
		{
			info_.push_back(std::to_string((long long)i));

			const Json::Value &json_value = json_array[i];

			value_stack_.push(&json_value);

			T value;
			// deserialize(value);
			try
			{
				deserialize(value);
			}
			catch (std::string e)
			{
				throw "Error: " + name + ":" + e;
			}
			values.push_back(value);

			value_stack_.pop();

			info_.pop_back();
		}

		info_.pop_back();
	}

	template <typename T>
	void deserializeOptional(std::string name, std::vector<T> &values)
	{
		info_.push_back(name);

		const Json::Value &json_parent = *value_stack_.top();

		// if (!json_parent.HasMember(name.c_str()))
		if (!json_parent.isMember(name))
		{
			std::cout << "!!! warning, deserialize,has no merber:" << name << std::endl;
			return;
			// std::string err_info = "Load json file [" + file_path_ + "] error, [" + GetParsePath() + "] doesn't exist!";
			// throw err_info;
		}

		const Json::Value &json_array = json_parent[name.c_str()];

		if (!json_array.isArray())
		{
			std::string err_info = "[" + name + "] is not an Array!";
			throw err_info;
		}

		for (int i = 0; i < json_array.size(); i++)
		{
			info_.push_back(std::to_string((long long)i));

			const Json::Value &json_value = json_array[i];

			value_stack_.push(&json_value);

			T value;
			// deserialize(value);
			try
			{
				deserialize(value);
			}
			catch (std::string e)
			{
				throw "Error: " + name + ":" + e;
			}
			values.push_back(value);

			value_stack_.pop();

			info_.pop_back();
		}
		info_.pop_back();
	}

private:
	void deserialize(std::string &value)
	{
		const Json::Value &json_value = *value_stack_.top();

		if (!json_value.isString())
		{
			throw std::string("is not a String value!");
		}

		// value = Utf82Ansi(json_value.GetString());
		value = json_value.asCString();
	}

	void deserialize(int &value)
	{
		const Json::Value &json_value = *value_stack_.top();

		if (!json_value.isInt())
		{
			throw std::string("is not a Int value!");
		}

		value = json_value.asInt();
	}

	void deserialize(long &value)
	{
		const Json::Value &json_value = *value_stack_.top();

		if (!json_value.isInt64())
		{
			throw std::string("is not a Int64 value!");
		}

		value = (long)json_value.asInt64();
	}

	void deserialize(unsigned char &value)
	{
		const Json::Value &json_value = *value_stack_.top();

		if (!json_value.isUInt())
		{
			throw std::string("is not a UInt value!");
		}

		value = json_value.asUInt();
	}

	void deserialize(unsigned short &value)
	{
		const Json::Value &json_value = *value_stack_.top();

		if (!json_value.isUInt())
		{
			throw std::string("is not a UInt value!");
		}

		value = json_value.asUInt();
	}

	void deserialize(unsigned long &value)
	{
		const Json::Value &json_value = *value_stack_.top();

		if (!json_value.isUInt64())
		{
			throw std::string("is not a isUInt64 value!");
		}

		value = (unsigned long)json_value.asUInt64();
	}

	void deserialize(double &value)
	{
		const Json::Value &json_value = *value_stack_.top();

		if (!json_value.isDouble())
		{
			throw std::string("is not a Double value!");
		}

		value = json_value.asDouble();
	}

	void deserialize(bool &value)
	{
		const Json::Value &json_value = *value_stack_.top();

		if (!json_value.isBool())
		{
			throw std::string("is not a Bool value!");
		}

		value = json_value.asBool();
	}

	void deserialize(Object &object)
	{
		const Json::Value &json_obj = *value_stack_.top();

		if (json_obj.isInt())
		{
			object.type = Object::TYPE_INT;
			object.int_val = json_obj.asInt();
		}
		else if (json_obj.isString())
		{
			object.type = Object::TYPE_STRING;
			object.string_val = json_obj.asString();
		}
		else if (json_obj.isArray())
		{
			object.type = Object::TYPE_VECTOR;

			for (int i = 0; i < json_obj.size(); i++)
			{
				info_.push_back(std::to_string((long long)i));

				const Json::Value &json_obj_item = json_obj[i];

				value_stack_.push(&json_obj_item);

				Object obj;
				deserialize(obj);
				object.vector_val.push_back(obj);

				value_stack_.pop();

				info_.pop_back();
			}
		}
		else if (json_obj.isNull())
		{
			object.type = Object::TYPE_NULL;
		}
		else
		{
			std::string err_info = "is not a Int or String or Array or Null value!";
			throw err_info;
		}
	}

	template <typename T>
	void deserialize(T &value)
	{
		value.deserialize(*this);
	}

	template <typename T>
	void deserialize(std::vector<T> &values)
	{
		const Json::Value &json_array = *value_stack_.top();

		if (!json_array.isArray())
		{
			throw std::string("is not a Array value!");
		}

		for (int i = 0; i < json_array.size(); i++)
		{
			info_.push_back(std::to_string((long long)i));

			const Json::Value &json_value = json_array[i];

			value_stack_.push(&json_value);

			T value;
			deserialize(value);
			values.push_back(value);

			value_stack_.pop();

			info_.pop_back();
		}
	}

	// Json::Document document_;
	Json::Value document_;
	std::stack<const Json::Value *> value_stack_;

	std::list<std::string> info_;

	std::string file_path_;
	// BYTE file_encoding;

private:
	template <class T>
	void MakeArray(T **ptr, const size_t &count)
	{
		if (count == 0)
		{
			*ptr = nullptr;
		}
		else
		{
			*ptr = new T[count];
			memset(*ptr, 0, count * sizeof(T));
		}
	}
	template <class T>
	void DeleteArray(T **ptr)
	{
		if (*ptr != nullptr)
		{
			delete[] *ptr;
			*ptr = nullptr;
		}
	}
	// bool IsUtf8Encoding(const std::string& s);
	std::string GetParsePath();
	// std::string Utf82Ansi(const std::string& s);
};